package com.piece.mall.service;

import com.piece.core.framework.constant.ExceptionConstants;
import com.piece.core.framework.enums.StatusType;
import com.piece.core.framework.exception.ServerException;
import com.piece.core.framework.support.convert.Convert;
import com.piece.core.framework.support.dto.PayloadDTO;
import com.piece.core.framework.util.collection.MapUtil;
import com.piece.core.framework.util.math.SequenceUtil;
import com.piece.core.framework.util.message.MessageFactory;
import com.piece.core.framework.util.object.BeanUtil;
import com.piece.core.framework.util.string.JsonUtil;
import com.piece.core.framework.util.string.StringUtil;
import com.piece.core.framework.util.time.DateUtil;
import com.piece.core.jpa.repository.BaseService;
import com.piece.core.log.client.dto.UserDTO;
import com.piece.core.log.config.UserContextHolder;
import com.piece.core.log.enums.MessageLogType;
import com.piece.core.mongo.entity.MongoMessageLog;
import com.piece.core.pay.dto.PayOrderUnifiedReqDTO;
import com.piece.mall.api.constant.MallConstant;
import com.piece.mall.api.constant.MallRegisterConstants;
import com.piece.mall.api.enums.OrderStatusType;
import com.piece.mall.model.*;
import com.piece.mall.repository.OmsOrderRepository;
import com.piece.mall.vo.CartItemVo;
import com.piece.mall.vo.OrderDetailVo;
import com.piece.mall.vo.OrderSubmitVo;
import com.piece.mall.vo.SeckillOrderVo;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.stream.Collectors;

@Slf4j
@Service
@Transactional
public class OmsOrderService extends BaseService<OmsOrder, String> {

    /** 自动确认时间 */
    public static final int autoConfirmDay = 7;
    private ThreadLocal<OrderSubmitVo> confirmOrderContext = new ThreadLocal<>();

    @Autowired
    private OmsOrderRepository omsOrderRepository;

    @Resource
    private OmsOrderItemService omsOrderItemService;

    @Resource
    private PmsSpuInfoService pmsSpuInfoService;

    @Resource
    private PmsSkuInfoService pmsSkuInfoService;

    @Resource
    private PmsBrandService pmsBrandService;

    @Resource
    private WmsWareSkuService wmsWareSkuService;

    @Resource
    private ThreadPoolTaskExecutor taskExecutor;

    @Resource
    private MessageFactory messageFactory;

    @Resource
    private RedisTemplate redisTemplate;

    @PostConstruct
    public void initRepository() {
        setRepository(omsOrderRepository);
    }

    public OrderDetailVo orderDetail(Long orderId) {
        if (null == orderId || orderId == 0) {
            throw new ServerException(ExceptionConstants.NOT_EXIST, "订单[" + Convert.toStr(orderId, "") + "]");
        }
        OmsOrder order = findById(orderId);
        OrderDetailVo detail = new OrderDetailVo();
        BeanUtil.copyProperties(order, detail, true);
        List<OmsOrderItem> items = omsOrderItemService.findByCondition(MapUtil.builder(new HashMap<String, Object>())
                .put("orderSn", order.getOrderSn()).build());
        List<CartItemVo> carts = new ArrayList<>();
        for (OmsOrderItem item: items) {
            CartItemVo cart = new CartItemVo();
            cart.setSkuId(item.getSkuId());
            cart.setTitle(item.getSkuName());
            cart.setImage(item.getSkuPic());
            cart.setPrice(item.getSkuPrice());
            cart.setCount(item.getSkuQuantity());
            cart.setTotalPrice(item.getRealAmount());
            if (StringUtil.isNotEmpty(item.getSkuAttrsValue())) {
                List<PmsSkuSaleAttrValue> saleAttrs = JsonUtil.toList(item.getSkuAttrsValue(), PmsSkuSaleAttrValue.class);
                cart.setSaleAttrValues(saleAttrs);
            } else {
                cart.setSaleAttrValues(Collections.EMPTY_LIST);
            }
            carts.add(cart);
        }
        detail.setCarts(carts);
        return detail;
    }

    /**
     * 生成确认订单详情
     */
    public OrderSubmitVo convertOrderSubmit(List<PmsSkuInfo> skuInfos, String subject, String body, String remark) {
        OrderSubmitVo submit = new OrderSubmitVo();
        submit.setToken(SequenceUtil.uuid());
        List<CartItemVo> cartItems = pmsSkuInfoService.buildCartItem(skuInfos);
        submit.setCarts(cartItems);
        BigDecimal payPrice = BigDecimal.ZERO;
        for (CartItemVo item : cartItems) {
            payPrice = payPrice.add(item.getTotalPrice());
        }
        submit.setPayPrice(payPrice);
        submit.setSubject(subject);
        submit.setBody(body);
        submit.setRemarks(remark);
        return submit;
    }

    /**
     * 提交订单
     */
    public OmsOrder submitOrder(OrderSubmitVo orderSubmit) throws Exception {
        confirmOrderContext.set(orderSubmit);
        UserDTO user = UserContextHolder.get();
//        String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
//        Long result = (Long) redisTemplate.execute(new DefaultRedisScript<>(script, Long.class), Arrays.asList(MallConstant.TOKEN_PREFIX + user.getId()), orderSubmit.getToken());
//        if (result == 0L) {
//            throw new ServerException("订单令牌验证失败");
//        }
        // 生成订单号
        String orderSn = SequenceUtil.next() + "";
        // 生成订单实体
        OmsOrder order = buildOrder(orderSn);
        order.setPayType(orderSubmit.getPayChannel());
        order.setChannelCode(orderSubmit.getChannelCode());
        // 生成订单项实体
        List<OmsOrderItem> orderItems = buildOrderItems(orderSn);
        // 汇总封装
        summaryFillOrder(order, orderItems);

        // 虚拟物品金额为0，支付金额自定义
        if (order.getPayAmount().equals(BigDecimal.ZERO)) {
            order.setPayAmount(orderSubmit.getPayPrice());
        } else if (Math.abs(orderSubmit.getPayPrice().subtract(order.getPayAmount()).doubleValue()) >= 0.01) {
            // 验价应付金额,前后端计算不一致允许0.01误差
            throw new ServerException("订单价格发生变化，请重新下单");
        }
        // 保存订单
        insert(order);
        omsOrderItemService.batchInsert(orderItems);
        // 锁定库存
        orderItems.stream().forEach(item -> wmsWareSkuService.lockStock(orderSn, item.getSkuId(), item.getSkuQuantity()));
        // 发送自动关闭订单通知
        sendCommitOrder(order);
        return order;
    }

    /**
     * 发送支付超时自动关闭订单通知
     */
    private void sendCommitOrder(OmsOrder order) {
        try {
            MongoMessageLog messageLog = new MongoMessageLog();
            messageLog.setTitle("创建待支付订单通知");
            messageLog.setEntity(JsonUtil.toJson(order));
            messageLog.setType(MessageLogType.MESSAGE.getType() + "");
            messageLog.setExchange(MallRegisterConstants.ORDER_DELAY_EXCHANGE_NAME);
            messageLog.setRouter(MallRegisterConstants.ORDER_CREATE_ROUTE_NAME);
            messageLog.setRetryCount(-1);
            PayloadDTO payload = new PayloadDTO();
            payload.setEntity(JsonUtil.toJson(messageLog));
            payload.setExchange(MallRegisterConstants.ORDER_DELAY_EXCHANGE_NAME);
            payload.setRouter(MallRegisterConstants.ORDER_CREATE_ROUTE_NAME);
            messageFactory.get().send(payload);
        } catch (Exception e) {
            log.error("创建订单发送消息失败：{}", e.getMessage());
        }
    }

    /**
     * 生成订单实体
     */
    private OmsOrder buildOrder(String orderSn) {
        OmsOrder order = new OmsOrder();
        // 封装用户
        order.setUserId(Convert.toLong(UserContextHolder.getUserId(), null));
        order.setUsername(UserContextHolder.getUserName());
        // 封装订单号
        order.setOrderSn(orderSn);
        // 封装运费
        order.setFreightAmount(BigDecimal.ZERO);
        // 封装收费地址 TODO
        // 封装自动确认时间
        order.setAutoConfirmDay(autoConfirmDay);
        // 封装订单待付款状态
        order.setStatus(OrderStatusType.PREPARE.getCode());
        // 封装订单有效状态
        order.setDeleteStatus(StatusType.NORMAL.getCode());
        // 默认开电子发票
        order.setBillType("1");
        // 封装创建时间
        Date date = new Date();
        order.setCreateTime(date);
        order.setModifyTime(date);

        return order;
    }

    /**
     * 生成订单项实体
     */
    private List<OmsOrderItem> buildOrderItems(String orderSn) {
        OrderSubmitVo orderSubmit = confirmOrderContext.get();
        List<CartItemVo> cartItems = orderSubmit.getCarts();
        if (!CollectionUtils.isEmpty(cartItems)) {
            ThreadPoolExecutor executor = taskExecutor.getThreadPoolExecutor();
            List<CompletableFuture> futures = new ArrayList<>();
            cartItems.stream().forEach(cartItem -> {
                CompletableFuture<OmsOrderItem> orderItemFuture = CompletableFuture.supplyAsync(() -> buildOrderItem(orderSn, cartItem), executor);
                futures.add(orderItemFuture);
            });

            List<OmsOrderItem> orderItems = futures.stream().map(CompletableFuture<OmsOrderItem>::join)
                    .collect(Collectors.toList());
            return orderItems;
        }

        throw new ServerException(ExceptionConstants.NOT_EXIST, "购物项");
    }

    private OmsOrderItem buildOrderItem(String orderSn, CartItemVo cartItem) {
        OmsOrderItem orderItem = new OmsOrderItem();
        // 封装订单号
        orderItem.setOrderSn(orderSn);
        // 封装spu
        PmsSkuInfo skuInfo = pmsSkuInfoService.findById(cartItem.getSkuId());
        PmsSpuInfo spuInfo = pmsSpuInfoService.findById(skuInfo.getSpuId());
        orderItem.setSpuId(spuInfo.getId());
        orderItem.setSpuName(spuInfo.getSpuName());
        String brandName = pmsBrandService.findNameById(spuInfo.getBrandId());
        orderItem.setSpuBrand(brandName);
        orderItem.setCategoryId(spuInfo.getCatalogId());
        // 封装sku
        orderItem.setSkuId(skuInfo.getSkuId());
        orderItem.setSkuName(skuInfo.getSkuName());
        orderItem.setSkuPic(cartItem.getImage());
        orderItem.setSkuPrice(cartItem.getPrice());
        orderItem.setSkuQuantity(cartItem.getCount());
        String saleAttrs = JsonUtil.toJson(cartItem.getSaleAttrValues());
        orderItem.setSkuAttrsValue(saleAttrs);
        // 封装优惠信息 TODO
        // 封装积分信息 分值=单价*数量
        long num = cartItem.getPrice().multiply(new BigDecimal(cartItem.getCount())).longValue();
        orderItem.setGiftGrowth(num);
        orderItem.setGiftIntegration(num);
        // 封装价格
        // 促销金额
        orderItem.setPromotionAmount(BigDecimal.ZERO);
        // 优惠券金额
        orderItem.setCouponAmount(BigDecimal.ZERO);
        // 积分优惠金额
        orderItem.setIntegrationAmount(BigDecimal.ZERO);
        // 封装实际金额
        BigDecimal realAmount = cartItem.getPrice().multiply(new BigDecimal(cartItem.getCount()))
                .subtract(orderItem.getPromotionAmount())
                .subtract(orderItem.getCouponAmount())
                .subtract(orderItem.getIntegrationAmount());
        orderItem.setRealAmount(realAmount);
        return orderItem;
    }

    /**
     * 汇总封装订单
     */
    private void summaryFillOrder(OmsOrder order, List<OmsOrderItem> items) {
        // 1.订单总额、促销总金额、优惠券总金额、积分优惠总金额
        BigDecimal total = new BigDecimal(0);
        BigDecimal coupon = new BigDecimal(0);
        BigDecimal promotion = new BigDecimal(0);
        BigDecimal integration = new BigDecimal(0);
        // 2.积分、成长值
        Long giftIntegration = 0L;
        Long giftGrowth = 0L;

        for (OmsOrderItem item : items) {
            // 订单总额
            total = total.add(item.getRealAmount());
            // 促销总金额
            coupon = coupon.add(item.getCouponAmount());
            // 优惠券总金额
            promotion = promotion.add(item.getPromotionAmount());
            // 积分优惠总金额
            integration = integration.add(item.getIntegrationAmount());
            // 积分
            giftIntegration = giftIntegration + item.getGiftIntegration();
            // 成长值
            giftGrowth = giftGrowth + item.getGiftGrowth();
        }

        order.setTotalAmount(total);
        order.setCouponAmount(coupon);
        order.setPromotionAmount(promotion);
        order.setIntegrationAmount(integration);
        order.setIntegration(giftIntegration);
        order.setGrowth(giftGrowth);
        order.setPayAmount(order.getTotalAmount().add(order.getFreightAmount()));
    }

    public PayOrderUnifiedReqDTO buildPayOrderUnified(OmsOrder order, String ip, String subject, String body) {
        PayOrderUnifiedReqDTO payOrder = new PayOrderUnifiedReqDTO();
        payOrder.setUserIp(ip);
        payOrder.setExpireTime(DateUtil.addHour(2));
        payOrder.setMerchantOrderId(order.getOrderSn());
        payOrder.setSubject(subject);
        payOrder.setBody(body);
        payOrder.setAmount(order.getPayAmount().longValue());
        return payOrder;
    }

    public OmsOrder findByOrderSn(String orderSn) {
        return omsOrderRepository.findOneByCondition(MapUtil.builder(new HashMap<String, Object>())
                .put("Q_orderSn_S_EQ", orderSn)
                .put("Q_deleteStatus_S_EQ", StatusType.NORMAL.getCode())
                .build());
    }

    public void changeOrderStatusByOrderSn(String orderSn, OrderStatusType statusType) {
        OmsOrder omsOrder = findByOrderSn(orderSn);
        if (null != omsOrder) {
            omsOrder.setStatus(statusType.getCode());
        } else {
            throw new ServerException(ExceptionConstants.NOT_EXIST, "订单号[" + orderSn + "]");
        }
    }

    /**
     * 关闭订单
     */
    public void closeOrder(String orderJson) {
        try {
            OmsOrder order = JsonUtil.toBean(orderJson, OmsOrder.class);
            order = findByOrderSn(order.getOrderSn());
            // 待付款订单允许关闭
            if (OrderStatusType.PREPARE.getCode().equals(order.getStatus())) {
                order.setStatus(OrderStatusType.CANCEL.getCode());
                update(order);
                // 释放库存
                unlockStock(order);
            }
        } catch (Exception e) {
            throw new ServerException("关闭订单异常：" + e.getMessage());
        }
    }

    @Override
    public void delete(Serializable id) throws Exception {
        OmsOrder order = findById(id);
        if (OrderStatusType.PREPARE.getCode().equals(order.getStatus())) {
            // 释放库存
            unlockStock(order);
        }
        // 删除订单
        order.setDeleteStatus(StatusType.DELETE.getCode());
        order.setModifyTime(new Date());
        omsOrderRepository.update(order);
    }

    /**
     * 释放订单商品库存
     */
    private void unlockStock(OmsOrder order) {
        List<OmsOrderItem> items = omsOrderItemService.findByCondition(MapUtil.builder(new HashMap<String, Object>())
                .put("orderId", order.getId()).build());
        for (OmsOrderItem item: items) {
            // 释放库存
            wmsWareSkuService.unLockStock(order, item.getSkuId());
        }
    }

    /**
     * 创建秒杀订单
     */
    public void createSeckillOrder(String orderJson) throws Exception {
        SeckillOrderVo seckillOrder = JsonUtil.toBean(orderJson, SeckillOrderVo.class);
        // 1.创建订单
        OmsOrder order = new OmsOrder();
        order.setOrderSn(seckillOrder.getOrderSn());
        // 封装用户
        order.setUserId(Convert.toLong(seckillOrder.getUserId(), null));
        order.setUsername(seckillOrder.getUserName());
        // 封装自动确认时间
        order.setAutoConfirmDay(autoConfirmDay);
        // 封装运费
        order.setFreightAmount(BigDecimal.ZERO);
        // 应付总额
        BigDecimal totalPrice = seckillOrder.getSeckillPrice().multiply(BigDecimal.valueOf(seckillOrder.getNum()));
        // 订单总额
        order.setTotalAmount(totalPrice);
        // 应付总额
        order.setPayAmount(totalPrice);
        // 封装订单待付款状态
        order.setStatus(OrderStatusType.PREPARE.getCode());
        // 封装订单有效状态
        order.setDeleteStatus(StatusType.NORMAL.getCode());
        // 默认开电子发票
        order.setBillType("1");
        // 封装创建时间
        Date date = new Date();
        order.setCreateTime(date);
        order.setModifyTime(date);
        // 保存订单
        insert(order);

        // 2.创建订单项信息
        OmsOrderItem orderItem = new OmsOrderItem();
        // 封装订单号
        orderItem.setOrderSn(seckillOrder.getOrderSn());
        orderItem.setRealAmount(totalPrice);
        orderItem.setSkuQuantity(seckillOrder.getNum());
        // 查询商品spu信息
        PmsSpuInfo spuInfo = pmsSpuInfoService.findBySku(seckillOrder.getSkuId());
        orderItem.setSpuId(spuInfo.getId());
        orderItem.setSpuName(spuInfo.getSpuName());
        orderItem.setSpuBrand(spuInfo.getBrandName());
        orderItem.setCategoryId(spuInfo.getCatalogId());
        omsOrderItemService.insert(orderItem);
        // 发送自动关闭订单通知
        sendCommitOrder(order);
    }
}
